﻿using System.Collections.Generic;
using System.Linq;
using System;
using System.Reflection.Emit;
using System.Web.Services.Protocols;
using System.Reflection;

namespace Axial
{
	public static class Extensions
	{
		public static IEnumerable<T> Subset<T>(this IEnumerable<T> enumerable, int skip, int take)
		{
			return enumerable.Skip(skip).Take(take);
		}

		public static int IndexOf<T>(this IEnumerable<T> enumerable, T value)
		{
			int i = 0;
			foreach (var t in enumerable)
			{
				if (t.Equals(value))
					return i;
				++i;
			}
			return -1;
		}

		public static bool IsJSSafe(this Type type)
		{
			return type.GetCustomAttributes(typeof(JSSafeAttribute), false).Length > 0;
		}

		public static string LowerFirstLetter(this string str)
		{
			return Char.ToLower(str[0]) + str.Substring(1);
		}

		public static string RemoveOuterCharacters(this string str)
		{
			return str.Substring(1, str.Length - 2);
		}

		public static int CountCharacters(this string str, char match)
		{
			int count = 0;
			foreach (char c in str)
				if (match == c)
					count++;
			return count;
		}

		public static bool IsStoreCode(this ILInstruction inst)
		{
			// BUG: OpCodes.Initobj will store in the address on top of the stack
			return inst.Code.Name.StartsWith("st");
		}
		public static int GetStoreLocation(this ILInstruction inst)
		{
			if (inst.Code == OpCodes.Stloc || inst.Code == OpCodes.Stloc_S) return (int)(byte)inst.Operand;
			else if (inst.Code == OpCodes.Stloc_0) return 0;
			else if (inst.Code == OpCodes.Stloc_1) return 1;
			else if (inst.Code == OpCodes.Stloc_2) return 2;
			else if (inst.Code == OpCodes.Stloc_3) return 3;
			return -1;
		}
		public static bool IsLoadCode(this ILInstruction inst)
		{
			return inst.Code.Name.StartsWith("ld");
		}
		public static int GetLoadLocation(this ILInstruction inst)
		{
			if (inst.Code == OpCodes.Ldloc || inst.Code == OpCodes.Ldloc_S) return (int)(byte)inst.Operand;
			else if (inst.Code == OpCodes.Ldloc_0) return 0;
			else if (inst.Code == OpCodes.Ldloc_1) return 1;
			else if (inst.Code == OpCodes.Ldloc_2) return 2;
			else if (inst.Code == OpCodes.Ldloc_3) return 3;
			return -1;
		}
		public static bool IsBrTrue(this ILInstruction inst)
		{
			return inst.Code == OpCodes.Brtrue_S || inst.Code == OpCodes.Brtrue;
		}
		public static bool IsBrFalse(this ILInstruction inst)
		{
			return inst.Code == OpCodes.Brfalse_S || inst.Code == OpCodes.Brfalse;
		}
		public static bool IsUnconditionalBranch(this ILInstruction inst)
		{
			return inst.Code.FlowControl == FlowControl.Branch;
		}
		public static bool IsConditionalBranch(this ILInstruction inst)
		{
			return inst.Code.FlowControl == FlowControl.Cond_Branch;
		}
		public static bool IsBranch(this ILInstruction inst)
		{
			return new FlowControl[] { FlowControl.Branch, FlowControl.Cond_Branch }.Contains(inst.Code.FlowControl);
		}
		public static bool IsComparison(this ILInstruction inst)
		{
			return new OpCode[] { OpCodes.Ceq, OpCodes.Cgt, OpCodes.Cgt_Un, OpCodes.Clt, OpCodes.Clt_Un }.Contains(inst.Code);
		}
		public static int GetBranchTarget(this ILInstruction inst)
		{
			if (!inst.IsBranch())
				return -1;
			return (int)inst.Operand;
		}

		public static string GetDefaultValue(this Type type)
		{
			return type.IsValueType ? Activator.CreateInstance(type).ToString() : "null";
		}

		public static int GetStackChangeCount(this StackBehaviour behavior)
		{
			if (behavior.ToString().EndsWith("0") || behavior.ToString().StartsWith("Var"))
				return 0;
			return 1 + behavior.ToString().CountCharacters('_');
		}

		public static void JSCall(this SoapHttpClientProtocol service, string name,
			Action<object> successCallback, Action<object> failureCallback, params object[] parameters)
		{
			MethodInfo method = service.GetType().GetMethod(name);
			if (method == null)
				throw new Exception("The specified web service method doesn't exist.");
			if (method.GetCustomAttributes(typeof(System.Web.Services.Protocols.SoapDocumentMethodAttribute), false).Length == 0)
				throw new Exception("The web service method called must be a web service proxy method.");
			if (method.GetParameters().Length != parameters.Length)
				throw new Exception("The wrong number of parameters was called on this function.");
		}
	}
}
